package com.goldmantis.alitrip.common.aspect;

import com.goldmantis.alitrip.common.ReturnMessage;
import com.goldmantis.alitrip.common.UserThreadLocal;
import com.goldmantis.alitrip.common.annotation.Log;
import com.goldmantis.alitrip.common.annotation.LogMetaInfo;
import com.goldmantis.alitrip.common.sysenum.EnumLogCatagory;
import com.goldmantis.alitrip.common.sysenum.EnumOperationType;
import com.goldmantis.alitrip.pojo.SysLog;
import com.goldmantis.alitrip.pojo.UvSysUser;
import com.goldmantis.alitrip.service.SysLogService;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.RequestConditionHolder;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.StringTokenizer;


/**
 * @Author:Shmily
 * @Description:日志切面
 * 注意：切入点不能是@Pointcut("@annotation(com.goldmantis.alitrip.common.annotation.Log)") 这样会导致异常通知无法拦截
 * @Date:Create in 11:21 2018/2/26
 */
@Aspect
@Component
public class LogAspect {

    @Autowired
    private SysLogService sysLogService;

    private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);


    /**
     * Controller层切点
     * 第一个*代表所有的返回值类型
     * 第二个*代表所有的类
     * 第三个*代表类所有方法
     * 最后一个..代表所有的参数。
     */
    @Pointcut("execution (* com.goldmantis.alitrip.controller..*.*(..))")
    public void controllerAspect() {

    }

    /**
     * 前置通知 用于拦截Controller层记录用户的操作
     *
     * @param joinPoint 切点
     */
    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint) {

        try {
            LogMetaInfo logMetaInfo = getLogMetaInfo(joinPoint);
            if(logMetaInfo==null){
                return;
            }

            System.out.println("----- Controller 前置通知Begin -----");

            SysLog sysLog = getBaseSysLog();
            sysLog.setCategoryId(logMetaInfo.getLogCatagory().getValue());
            sysLog.setDescription(logMetaInfo.getOperationName());
            sysLog.setOperateTypeId(String.valueOf(logMetaInfo.getOperationType().getValue()));
            sysLog.setOperateType(logMetaInfo.getOperationType().getText());
            this.sysLogService.insert(sysLog);

            String accessMethod = (joinPoint.getTarget().getClass().getName()).concat(".")
                    + joinPoint.getSignature().getName().concat("().");

            System.out.println("请求方法：" + accessMethod);
            System.out.println("方法描述：" + logMetaInfo.getOperationName());
            System.out.println("请求人：" + sysLog.getOperateAccount());
            System.out.println("请求IP：" + sysLog.getIpAddress());
            System.out.println("请求主机：" + sysLog.getHost());

            System.out.println("----- Controller 前置通知End -----");
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error(e.getMessage());
        }
    }


    @AfterThrowing(value = "controllerAspect()", throwing = "ex")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable ex) {
        System.out.println("----- Controller 异常通知Begin -----");

        try {

            LogMetaInfo logMetaInfo = getLogMetaInfo(joinPoint);
            SysLog sysLog = getBaseSysLog();

            sysLog.setCategoryId(EnumLogCatagory.exception.getValue());
            sysLog.setOperateTypeId(String.valueOf(EnumOperationType.exception.getValue()));
            sysLog.setOperateType(EnumOperationType.exception.getText());

            sysLog.setDescription(logMetaInfo.getOperationName());
            sysLog.setExecuteResult(-1);
            sysLog.setExecuteResultJson(prepareExceptionInfo(joinPoint,ex));


            sysLog.setExceptionType(ex.toString());
            this.sysLogService.insert(sysLog);
            ex.printStackTrace();

        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error(e.getMessage());
        }

        System.out.println("----- Controller 异常通知End -----");
    }

    private SysLog getBaseSysLog(){

        //获取httpRequestServlet
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();

        SysLog sysLog = new SysLog();
        sysLog.setOperateTime(new Date());
        sysLog.setIpAddress(request.getRemoteAddr());
        sysLog.setHost(request.getRemoteHost());
        //获取浏览器和操作系统
        sysLog.setBrowser(request.getHeader("user-agent"));
        sysLog.setxRealIp(request.getHeader("x-forwarded-for"));
        sysLog.setExecuteResult(1);
        sysLog.setExecuteResultJson(request.getRequestURL().toString());

        UvSysUser user = UserThreadLocal.get();
        if (user != null) {
            sysLog.setOperateUserId(user.userId);
            sysLog.setOperateAccount(user.getAccount().concat("(").concat(user.fullName).concat(")"));
        }

        return sysLog;

    }

    private LogMetaInfo getLogMetaInfo(JoinPoint joinPoint) throws ClassNotFoundException {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        Class<?> targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();


        LogMetaInfo logMetaInfo=new LogMetaInfo();
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == args.length) {
                    Log log=method.getAnnotation(Log.class);
                    if(log==null){
                        return null;
                    }
                    logMetaInfo.setOperationName(log.operationName());
                    logMetaInfo.setLogCatagory(log.logCatagory());
                    logMetaInfo.setOperationType(log.operationType());
                }
            }
        }

        return logMetaInfo;
    }

    private String prepareExceptionInfo(JoinPoint joinPoint, Throwable ex){
        StringBuilder sb=new StringBuilder();
        sb.append("异常代码：");
        sb.append(ex.getClass().getName());
        sb.append("\r\n");

        sb.append("异常方法：");
        sb.append((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
        sb.append("\r\n");

        sb.append("异常参数：");
        sb.append(StringUtils.join(joinPoint.getArgs(),","));
        sb.append("\r\n");

        sb.append("异常信息：");
        sb.append(ex.getMessage());
        sb.append("\r\n");

        sb.append("异常堆栈信息：");
        sb.append(StringUtils.join(ex.getStackTrace(),"\r\n"));
        sb.append("\r\n");

        return sb.toString();

    }
}
